use super::role::RoleSimpletVo;
use super::{PostSimpleVo, Result};
use sea_orm::sea_query::Cond;
use sys_dept::Column as DeptColumn;
use sys_user::Column as UserColumn;

use crate::entity::{sys_post, sys_user_post};
use crate::{
    entity::{sys_dept, sys_resource, sys_role, sys_role_resource, sys_user, sys_user_role},
    password,
};
use chrono::NaiveDateTime;
use sea_orm::Related;
use sea_orm::{
    ActiveModelTrait, ColumnTrait, Condition, ConnectionTrait, DbConn, DerivePartialModel,
    EntityTrait, FromQueryResult, JoinType, Order, PaginatorTrait, PartialModelTrait, QueryFilter,
    QueryOrder, QuerySelect, QueryTrait, RelationTrait, Set,
};
use serde::{Deserialize, Serialize};
use validator::Validate;

use crate::entity::sys_user::Entity as SystemUser;

use crate::entity::sys_user::Model as UserModel;

pub struct User;
pub type UserId = i32;

#[derive(Debug, DerivePartialModel, FromQueryResult)]
#[sea_orm(entity = "SystemUser")]
pub struct UserAvatar {
    pub user_id: i32,
    pub avatar: String,
}

#[derive(Debug, Clone, DerivePartialModel, FromQueryResult, Serialize, Deserialize)]
#[sea_orm(entity = "SystemUser")]
pub struct UserVo {
    pub user_id: UserId,
    pub dept_id: i32,
    #[sea_orm(from_expr = "crate::entity::sys_dept::Column::DeptName")]
    pub dept_name: Option<String>,
    pub username: String,
    pub nickname: String,
    pub phone: String,
    pub email: String,
    pub sex: i8,
    pub remark: String,
    /// false停用 true启用
    pub status: bool,
    pub created_at: NaiveDateTime,
    pub updated_at: NaiveDateTime,
}

/// 用户的详情数据, 包含全部可用角色
#[derive(Debug, Serialize)]
pub struct UserDetailVo {
    pub user_id: UserId,
    pub dept_id: i32,
    pub avatar: String,
    pub username: String,
    pub nickname: String,
    pub phone: String,
    pub email: String,
    pub sex: i8,
    pub remark: String,
    pub status: bool,
    pub created_at: NaiveDateTime,
    pub updated_at: NaiveDateTime,
    pub roles: Vec<RoleSimpletVo>,
    pub posts: Vec<PostSimpleVo>,
}

/// 用户简略的数据，用于 list 的展示
#[derive(Debug, DerivePartialModel, FromQueryResult, Serialize)]
#[sea_orm(entity = "SystemUser")]
pub struct UserSimpletVo {
    pub user_id: UserId,
    pub dept_id: i32,
    #[sea_orm(from_expr = "crate::entity::sys_dept::Column::DeptName")]
    pub dept_name: Option<String>,
    pub username: String,
    pub nickname: String,
    pub status: bool,
    pub created_at: NaiveDateTime,
}

/// 创建用户的数据
#[derive(Debug, Deserialize, Validate)]
pub struct NewUserDo {
    #[validate(length(min = 3, max = 32), non_control_character)]
    pub username: String,
    #[validate(length(min = 3, max = 32))]
    pub nickname: String,
    #[validate(length(min = 6, max = 32), non_control_character)]
    pub password: String,
    pub dept_id: i32,
    pub status: Option<bool>,
    #[validate(custom(function = "crate::util::validate_phone"))]
    pub phone: Option<String>,
    #[validate(custom(function = "crate::util::validate_email"))]
    pub email: Option<String>,
    pub sex: Option<i8>,
    #[validate(length(max = 255))]
    pub remark: Option<String>,
}

/// 修改用户的数据
#[derive(Debug, Deserialize, Default, Validate)]
pub struct UpdateUserDo {
    pub avatar: Option<String>,
    #[validate(length(min = 3, max = 32), non_control_character)]
    pub username: Option<String>,
    #[validate(length(min = 3, max = 32))]
    pub nickname: Option<String>,
    #[validate(length(min = 6, max = 32), non_control_character)]
    pub password: Option<String>,
    #[validate(custom(function = "crate::util::validate_phone"))]
    pub phone: Option<String>,
    #[validate(custom(function = "crate::util::validate_email"))]
    pub email: Option<String>,
    pub sex: Option<i8>,
    #[validate(length(max = 255))]
    pub remark: Option<String>,
    pub dept_id: Option<i32>,
    pub status: Option<bool>,
}

#[derive(Deserialize, Validate)]
pub struct ChangePasswordDo {
    #[validate(length(min = 6, max = 32), non_control_character)]
    old_pwd: String,
    #[validate(length(min = 6, max = 32), non_control_character)]
    new_pwd: String,
}

impl User {
    /// 根据id获得用户
    /// ## params
    ///  - `id: UserId` user的id
    /// - `state: Option<bool>` user的状态
    /// - `deleted: bool` 检查用户是否软删除。false未删除, ture软删除
    /// - `dept: Option<Cond>` 额外的条件Cond, 可检查用户是属于该单位
    pub async fn get_by_id<T>(
        db: &impl ConnectionTrait,
        id: UserId,
        status: Option<bool>,
        dept: Option<Cond>,
        deleted: bool,
    ) -> Result<Option<T>>
    where
        T: PartialModelTrait + Send + Sync,
    {
        sys_user::Entity::find_by_id(id)
            .filter(sys_user::Column::DelFlag.eq(deleted))
            .inner_join(sys_dept::Entity)
            .apply_if(status, |sql, v| sql.filter(sys_user::Column::Status.eq(v)))
            .apply_if(dept, |sql, dept| sql.filter(dept))
            .into_partial_model()
            .one(db)
            .await
    }

    /// 根据username获得用户
    /// ## params
    ///  - `name: &str` user的账户名username
    /// - `state: Option<bool>` user的状态
    /// - `deleted: bool` 检查用户是否软删除。false未删除, ture软删除
    /// - `dept: Option<Cond>` 额外的条件Cond, 可检查用户是属于该单位
    pub async fn get_by_name<T>(
        db: &impl ConnectionTrait,
        name: &str,
        status: Option<bool>,
        dept: Option<Cond>,
        deleted: bool,
    ) -> Result<Option<T>>
    where
        T: PartialModelTrait + Send + Sync,
    {
        sys_user::Entity::find()
            .filter(sys_user::Column::Username.eq(name))
            .filter(sys_user::Column::DelFlag.eq(deleted))
            .inner_join(sys_dept::Entity)
            .apply_if(status, |sql, v| sql.filter(sys_user::Column::Status.eq(v)))
            .apply_if(dept, |sql, dept| sql.filter(dept))
            .into_partial_model()
            .one(db)
            .await
    }

    /// 通过name和password获得一个用户
    /// # return
    /// Option<([sys_user::Model], bool)> 第二个参数表明用户密码是否正确
    pub async fn get_by_name_password(
        conn: &DbConn,
        name: &str,
        password: &str,
    ) -> Result<Option<(sys_user::Model, bool)>> {
        SystemUser::find()
            .filter(sys_user::Column::DelFlag.eq(false))
            .filter(sys_user::Column::Username.eq(name))
            .one(conn)
            .await
            .map(|user| {
                user.map(|user| {
                    let verify = crate::password::verify(password, &user.password).unwrap_or(false);
                    (user, verify)
                })
            })
    }

    /// 获取一个用户的详细信息, 包含**可用的角色**和单位
    /// # params
    /// - `status` 检查用户状态
    /// - `deleted` 检查用户是否软删除。false未删除, ture软删除
    /// - `dept:Option<Cond>` 检查用户是属于该单位
    pub async fn get_detail_by_id(
        db: &DbConn,
        id: UserId,
        status: Option<bool>,
        dept: Option<Cond>,
        deleted: bool,
    ) -> Result<Option<UserDetailVo>> {
        let user = if let Some(res) =
            Self::get_by_id::<sys_user::Model>(db, id, status, dept, deleted).await?
        {
            res
        } else {
            return Ok(None);
        };
        let roles = Self::get_roles(db, user.user_id).await?;
        let posts = Self::get_posts(db, user.user_id).await?;
        Ok(Some(UserDetailVo {
            user_id: user.user_id,
            dept_id: user.dept_id,
            avatar: user.avatar,
            username: user.username,
            nickname: user.nickname,
            status: user.status,
            created_at: user.created_at,
            updated_at: user.updated_at,
            roles,
            posts,
            phone: user.phone,
            email: user.email,
            sex: user.sex,
            remark: user.remark,
        }))
    }

    /// 获得角色的dept信息
    /// - `id:i32` 用户id
    pub async fn get_dept_info<T>(db: &impl ConnectionTrait, id: i32) -> Result<Option<T>>
    where
        T: PartialModelTrait + Send + Sync,
    {
        sys_user::Entity::find()
            .inner_join(sys_dept::Entity)
            .filter(sys_user::Column::UserId.eq(id))
            .filter(sys_dept::Column::DelFlag.eq(false))
            .into_partial_model()
            .one(db)
            .await
    }

    /// 获得用户的全部可用 resource
    /// ## return
    /// Vec<(Path,Method)>
    pub async fn get_resource(db: &impl ConnectionTrait, id: i32) -> Result<Vec<(String, String)>> {
        sys_user::Entity::find()
            .select_only()
            .column(sys_resource::Column::Path)
            .column(sys_role_resource::Column::Method)
            .join(
                JoinType::InnerJoin,
                <sys_user::Entity as Related<sys_user_role::Entity>>::to(),
            )
            .join(
                JoinType::InnerJoin,
                <sys_user_role::Entity as Related<sys_role::Entity>>::to(),
            )
            .join(
                JoinType::InnerJoin,
                sys_role::Relation::SysRoleResource.def(),
            )
            .join(
                sea_orm::JoinType::InnerJoin,
                <sys_role_resource::Entity as Related<sys_resource::Entity>>::to(),
            )
            .filter(sys_resource::Column::Status.eq(true))
            .filter(sys_role::Column::Status.eq(true))
            .filter(sys_user::Column::UserId.eq(id))
            .into_tuple::<(String, String)>()
            .all(db)
            .await
    }

    /// 查询一个用户的全部**可用**Post, `statue==true` `delflag==0`
    #[inline]
    pub async fn get_posts<T>(conn: &impl ConnectionTrait, user_id: UserId) -> Result<Vec<T>>
    where
        T: PartialModelTrait + Sync + Send,
    {
        sys_user_post::Entity::find()
            .inner_join(sys_post::Entity)
            .filter(sys_user_post::Column::UserId.eq(user_id))
            .filter(sys_post::Column::Status.eq(true))
            .filter(sys_post::Column::DelFlag.eq(0))
            .into_partial_model()
            .all(conn)
            .await
    }

    /// 查询一个用户的全部**可用**Role, `statue==true` `delflag==0`
    #[inline]
    pub async fn get_roles<T>(conn: &impl ConnectionTrait, user_id: UserId) -> Result<Vec<T>>
    where
        T: PartialModelTrait + Sync + Send,
    {
        sys_user_role::Entity::find()
            .inner_join(sys_role::Entity)
            .filter(sys_user_role::Column::UserId.eq(user_id))
            .filter(sys_role::Column::Status.eq(true))
            .filter(sys_role::Column::DelFlag.eq(0))
            .into_partial_model()
            .all(conn)
            .await
    }

    /// 列出所有用户
    ///
    /// # Params
    /// - page page_size 分页信息
    /// - `dept: Option<i32>`  是否从单位id中筛序, 包括本单位及以下部门
    /// - `cond: Option<Cond>` 可自定义条件，关联的表 sys_user sys_dept
    /// - `username: Option<String>`
    /// - `nickname: Option<String>`
    /// - `status: Option<bool>`
    /// - `order: Option<bool>` 是否启用排序,根据创建时间排序
    pub async fn list<T>(
        db: &DbConn,
        page: u64,
        page_size: u64,
        dept: Option<i32>,
        cond: Option<Cond>,
        username: Option<String>,
        nickname: Option<String>,
        status: Option<bool>,
        order: Option<bool>,
    ) -> Result<(Vec<T>, u64)>
    where
        T: PartialModelTrait + Sync + Send,
    {
        let mut sql = sys_user::Entity::find()
            .left_join(sys_dept::Entity)
            .filter(UserColumn::DelFlag.eq(false))
            .filter(DeptColumn::DelFlag.eq(false))
            .apply_if(nickname, |sql, v| {
                sql.filter(UserColumn::Nickname.contains(v))
            })
            .apply_if(username, |sql, v| {
                sql.filter(UserColumn::Username.contains(v))
            })
            .apply_if(status, |sql, v| sql.filter(UserColumn::Status.eq(v)))
            .apply_if(order, |sql, asc| {
                sql.order_by(
                    UserColumn::CreatedAt,
                    if asc { Order::Asc } else { Order::Desc },
                )
            })
            .apply_if(cond, |sql, cond| sql.filter(cond));
        if let Some(dept) = dept {
            // 查找单位是否存在且可用
            let dept = if let Some(dept) = sys_dept::Entity::find_by_id(dept)
                .filter(sys_dept::Column::DelFlag.eq(0))
                .one(db)
                .await?
            {
                dept
            } else {
                return Ok((vec![], 0));
            };
            let child_ancestors = format!("{}{}/", dept.ancestors, dept.dept_id);
            sql = sql
                .filter(sys_dept::Column::Status.eq(true))
                .filter(sys_dept::Column::DelFlag.eq(0))
                .filter(
                    Condition::any()
                        .add(UserColumn::DeptId.eq(dept.dept_id))
                        .add(sys_dept::Column::Ancestors.starts_with(child_ancestors)),
                );
        }
        let sql = sql.into_partial_model().paginate(db, page_size);
        let total = sql.num_items().await?;
        let users = sql.fetch_page(page).await?;
        Ok((users, total))
    }

    /// 创建一个用户
    /// - 不检查dept和role关系的有效性
    /// - 不检查username的重名
    #[inline]
    pub async fn create(db: &impl ConnectionTrait, data: NewUserDo) -> Result<UserModel> {
        sys_user::ActiveModel {
            dept_id: Set(data.dept_id),
            username: Set(data.username),
            nickname: Set(data.nickname),
            password: Set(data.password),
            avatar: Set("".to_owned()),
            status: Set(data.status.unwrap_or(true)),
            del_flag: Set(false),
            phone: Set(data.phone.unwrap_or_default()),
            email: Set(data.email.unwrap_or_default()),
            sex: Set(data.sex.unwrap_or_default()),
            remark: Set(data.remark.unwrap_or_default()),
            ..Default::default()
        }
        .insert(db)
        .await
    }

    /// 修改一个用户
    /// - 不检查dept和role关系的有效性
    /// - 不检查用户是否存在
    #[inline]
    pub async fn update(
        db: &impl ConnectionTrait,
        user_id: UserId,
        data: UpdateUserDo,
    ) -> Result<UserModel> {
        sys_user::ActiveModel {
            user_id: Set(user_id),
            dept_id: data.dept_id.map(Set).unwrap_or_default(),
            avatar: data.avatar.map(Set).unwrap_or_default(),
            username: data.username.map(Set).unwrap_or_default(),
            nickname: data.nickname.map(Set).unwrap_or_default(),
            status: data.status.map(Set).unwrap_or_default(),
            phone: data.phone.map(Set).unwrap_or_default(),
            email: data.email.map(Set).unwrap_or_default(),
            sex: data.sex.map(Set).unwrap_or_default(),
            remark: data.remark.map(Set).unwrap_or_default(),
            password: data.password.map(Set).unwrap_or_default(),
            ..Default::default()
        }
        .update(db)
        .await
    }

    /// 更新 sys_user_role 表
    ///
    /// 不检查 user_id 和 roles id 是否存在
    pub async fn update_roles(
        txn: &impl ConnectionTrait,
        user_id: UserId,
        roles: Vec<i32>,
    ) -> Result<()> {
        let roles = roles
            .into_iter()
            .map(|r_id| sys_user_role::ActiveModel {
                role_id: Set(r_id),
                user_id: Set(user_id.clone()),
            })
            .collect::<Vec<_>>();
        sys_user_role::Entity::delete_many()
            .filter(sys_user_role::Column::UserId.eq(user_id))
            .exec(txn)
            .await?;
        if roles.len() > 0 {
            sys_user_role::Entity::insert_many(roles).exec(txn).await?;
        }
        Ok(())
    }

    /// 更新 sys_user_post 表
    ///
    /// 不检查 user_id 和 posts id 是否存在
    pub async fn update_posts(
        txn: &impl ConnectionTrait,
        user_id: UserId,
        posts: Vec<i32>,
    ) -> Result<()> {
        let posts = posts
            .into_iter()
            .map(|p_id| sys_user_post::ActiveModel {
                post_id: Set(p_id),
                user_id: Set(user_id.clone()),
            })
            .collect::<Vec<_>>();
        sys_user_post::Entity::delete_many()
            .filter(sys_user_post::Column::UserId.eq(user_id))
            .exec(txn)
            .await?;
        if posts.len() > 0 {
            sys_user_post::Entity::insert_many(posts).exec(txn).await?;
        }
        Ok(())
    }

    /// 直接修改用户密码
    pub async fn change_password(db: &DbConn, user_id: UserId, new_psd: String) -> Result<bool> {
        sys_user::ActiveModel {
            user_id: Set(user_id),
            password: Set(new_psd),
            ..Default::default()
        }
        .update(db)
        .await
        .map(|_| true)
    }

    /// 修改用户密码
    ///
    /// 当用户不存在, 未启用，软删除，原密码不正确，返回 false
    pub async fn change_password2(
        db: &DbConn,
        user_id: UserId,
        data: ChangePasswordDo,
    ) -> Result<bool> {
        let self_password = if let Some(pwd) = sys_user::Entity::find_by_id(user_id)
            .select_only()
            .column(sys_user::Column::Password)
            .filter(sys_user::Column::Status.eq(true))
            .filter(sys_user::Column::DelFlag.eq(false))
            .into_tuple::<String>()
            .one(db)
            .await?
        {
            pwd
        } else {
            return Ok(false);
        };
        if !Self::check_password(&self_password, &data.old_pwd) {
            return Ok(false);
        }
        sys_user::ActiveModel {
            user_id: Set(user_id),
            password: Set(data.new_pwd),
            ..Default::default()
        }
        .update(db)
        .await
        .map(|_| true)
    }

    /// 软删除用户, 将del_flag字段设置为[true]
    ///
    /// 用户不存在抛出错误 [DbErr::RecordNotUpdated](sea_orm::DbErr::RecordNotUpdated)
    #[inline]
    pub async fn soft_delete(db: &DbConn, user_id: UserId) -> Result<bool> {
        sys_user::ActiveModel {
            user_id: Set(user_id),
            del_flag: Set(true),
            ..Default::default()
        }
        .update(db)
        .await
        .map(|_| true)
    }

    /// 删除用户
    #[inline]
    pub async fn delete(db: &impl ConnectionTrait, user_id: UserId) -> Result<u64> {
        sys_user_role::Entity::delete_many()
            .filter(sys_user_role::Column::UserId.eq(user_id))
            .exec(db)
            .await?;
        sys_user::Entity::delete_by_id(user_id)
            .exec(db)
            .await
            .map(|r| r.rows_affected)
    }

    /// 检查用户名是否存在
    /// - username 检查长度不在3～32，直接返回false
    pub async fn check_name(db: &impl ConnectionTrait, username: &str) -> Result<bool> {
        if username.len() < 3 || username.len() > 32 {
            Ok(false)
        } else {
            SystemUser::find()
                .filter(sys_user::Column::Username.eq(username))
                .filter(sys_user::Column::DelFlag.eq(false))
                .count(db)
                .await
                .map(|f| f > 0)
        }
    }

    /// 检查密码是否正确
    /// - hashed_pwd 数据库保存的hash密码
    /// - pwd 要验证的密码原文
    #[inline]
    pub fn check_password(hashed_pwd: &str, pwd: &str) -> bool {
        password::verify(pwd, &hashed_pwd).unwrap_or(false)
    }
}
